using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Threading;
using MacomberMap.Common.Internals;
using System.IO.Compression;
using MacomberMap.Common.Components;
using MacomberMap.Common.Database_Interface.CIM.Components;

namespace MacomberMap.Common.Database_Interface.CIM.CIM_Interface
{
    /// <summary>
    /// This class handles the opening of a CIM Index file 
    /// </summary>
    public class CIM_Index_Reader : CIM_File_Interface
    {
        #region Variable Declaration
        private FileStream IndexReader;

        /// <summary>The thread responsible for managing the CIM loading process</summary>
        private Thread CIMLoaderThread;       
        #endregion

        #region Position tracking
        /// <summary>
        /// Return the file size of the index
        /// </summary>
        public override long FileSize
        {
            get
            {
                if (IndexReader == null)
                    return 0;
                else
                    return IndexReader.Length;
            }
        }

        /// <summary>
        /// Return our current position in the filestream
        /// </summary>
        public override long Position
        {
            get
            {
                if (IndexReader == null)
                    return 0;
                else
                    return IndexReader.Position;
            }
        }
        #endregion

        #region Initialization
        /// <summary>
        /// Initialize a new index reader manager
        /// </summary>
        /// <param name="FileName">The file name of the CIM index</param>        
        public CIM_Index_Reader(String FileName)
        {
            
            CIMLoaderThread = new Thread(new ParameterizedThreadStart(LoadCIMFile));
            CIMLoaderThread.Name = "CIM Index Reader Manager";
            CIMLoaderThread.SetApartmentState(ApartmentState.STA);
            CIMLoaderThread.Start(FileName);
        }

        

        /// <summary>
        /// Load a CIM indexed file into memory
        /// </summary>
        /// <param name="FileName">The file to be opened</param>
        public void LoadCIMFile(Object FileName)
        {
            //Track our startup time
            this.StartTime = DateTime.Now; 

            //Open our CIM file
            IndexReader = new FileStream((FileName as string), FileMode.Open, FileAccess.Read, FileShare.Read, MM_Configuration.FileBufferSize);

            //Load our header
            List<byte> inHeader = new List<byte>();
            byte inByte;
            while ((inByte = (byte)IndexReader.ReadByte())!= 10)
                inHeader.Add(inByte);                        
            String VersionInfo = Encoding.UTF8.GetString(inHeader.ToArray());

            //Load the rest of the file
            GZipStream gRd = new GZipStream(IndexReader, CompressionMode.Decompress);
            StreamReader sRd = new StreamReader(gRd, Encoding.UTF8);
            String InLine;

            //Now, read every line from the file.
            while ((InLine = sRd.ReadLine()) != null)
            {
                KeyValuePair<String, int> InVal = new KeyValuePair<string, int>(InLine.Split(':')[0], int.Parse(InLine.Split(':')[1]));
                switch (InVal.Key)
                {
                    //Ignore these points - informational, at least for now
                    case "Maximum rdfID length":
                    case "Maximum incoming links":
                    case "Maximum outgoing links":
                        break;
                    case "Maximum mRID":
                        MM_Database_CIM.MaxMRID = Convert.ToInt32(InVal.Value);
                        break;
                    case "Attributes":
                        for (int a = 0; a < InVal.Value; a++)                               
                        {
                            String inLine = sRd.ReadLine();
                            MM_Database_CIM.Attributes.Add(inLine);
                            MM_Database_CIM.TypesByAttribute.Add((ushort)a, new List<byte>());
                        }
                        //System.IO.StreamWriter sW = new System.IO.StreamWriter("C:\\attrib.csv"); foreach (String Attr in CIM_Element.Attributes) sW.WriteLine(Attr); sW.Close();
                        break;
                    case "Types":
                        for (int a = 0; a < InVal.Value; a++)
                        {
                            String[] splLine = sRd.ReadLine().Split(',');
                            MM_Database_CIM.Types.Add(splLine[0]);
                            byte TypeNumber = (byte)(MM_Database_CIM.Types.Count - 1);
                            List<ushort> OutLst = new List<ushort>(5);
                            for (int b = 1; b < splLine.Length; b++)
                                if (splLine[b] != "")
                                {
                                    ushort AttributeNumber = ushort.Parse(splLine[b]);
                                    OutLst.Add(AttributeNumber);
                                    if (!MM_Database_CIM.TypesByAttribute.ContainsKey(TypeNumber))
                                        MM_Database_CIM.TypesByAttribute.Add(AttributeNumber, new List<byte>(20));
                                    if (!MM_Database_CIM.TypesByAttribute[AttributeNumber].Contains(TypeNumber))
                                        MM_Database_CIM.TypesByAttribute[AttributeNumber].Add(TypeNumber);

                                }
                            MM_Database_CIM.AttributesByType.Add((byte)(MM_Database_CIM.Types.Count - 1), OutLst);

                        }
                        break;
                    case "TypeToTypes":
                        for (int a = 0; a < InVal.Value; a++)
                        {
                            String inLine = sRd.ReadLine();
                            String[] splLine = inLine.Split(',');
                            List<byte> OutLst = new List<byte>();
                            for (int b = 1; b < splLine.Length; b++)
                                if (splLine[b] != "")
                                    OutLst.Add(byte.Parse(splLine[b]));                                    
                            MM_Database_CIM.TypeToTypes.Add(byte.Parse(splLine[0]), OutLst);
                        }
                        break;
                    case "TypeFromTypes":
                        for (int a = 0; a < InVal.Value; a++)
                        {
                            String inLine = sRd.ReadLine();
                            String[] splLine = inLine.Split(',');
                            List<byte> OutLst = new List<byte>();
                            for (int b = 1; b < splLine.Length; b++)
                                if (splLine[b] != "")
                                    OutLst.Add(byte.Parse(splLine[b]));     
                            MM_Database_CIM.TypeFromTypes.Add(byte.Parse(splLine[0]), OutLst);
                        }
                        break;
                    case "Elements":
                        //Retrieve all elements
                        for (int a = 0; a < InVal.Value; a++)
                        {
                            String[] splLine = sRd.ReadLine().Split(',');
                            CIM_Element NewElem = MM_Database_CIM.AddCIMElement(splLine[0], long.Parse(splLine[2]), int.Parse(splLine[3]), splLine[1], "");
                            foreach (String inLink in splLine[4].Split('|'))
                                if (inLink != "")
                                    NewElem._InLinks.Add(int.Parse(inLink));
                            foreach (String outLink in splLine[5].Split('|'))
                                if (outLink != "")
                                    NewElem._OutLinks.Add(int.Parse(outLink));
                            NewElem._InLinks.TrimExcess();
                            NewElem._OutLinks.TrimExcess();
                            if (splLine.Length == 7)
                                NewElem.CheckSum = Convert.FromBase64String(splLine[6]);
                        }
                        break;
                    case "TEIDs":
                    case "tEIDs":
                        //Retrieve the tEID list.
                        MM_Database_CIM.TEIDs = new Dictionary<ulong, CIM_Element>(InVal.Value);
                        MM_Database_CIM.ReverseTEIDs = new Dictionary<CIM_Element, ulong>(InVal.Value);
                        for (int a = 0; a < InVal.Value; a++)
                        {
                            String[] splLine = sRd.ReadLine().Split(',');
                            MM_Database_CIM.TEIDs.Add(UInt32.Parse(splLine[0]), MM_Database_CIM.Elements[new MM_rdfID(splLine[1])]);
                            MM_Database_CIM.ReverseTEIDs.Add(MM_Database_CIM.Elements[new MM_rdfID(splLine[1])], UInt32.Parse(splLine[0]));
                        }
                        break;
                    default:
                        break;
                }                
            }           
            this.StartTime = DateTime.Now; 
            IndexReader.Close();
            IndexReader.Dispose();
            IndexReader = null;
            BuildElementsByType();
            Completed = true;
        }

       
        #endregion
    }
}
